package com.pulse.common.utils.sor;

import com.pulse.common.utils.sor.domain.Block;
import com.pulse.common.utils.sor.domain.FxdParamsVo;
import com.pulse.common.utils.sor.domain.SorInfo;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class FxdParams {
    private static final Map<String, String> UnitMap = new HashMap<String, String>() {{
        put("mt", " (meters)");
        put("km", " (kilometers)");
        put("mi", " (miles)");
        put("kf", " (kilo-ft)");
    }};

    private static final Map<String, String> TraceType = new HashMap<String, String>() {{
        put("ST", "[standard trace]");
        put("RT", "[reverse trace]");
        put("DT", "[difference trace]");
        put("RF", "[reference]");
    }};

    /**
     * 处理 FxdParams 块
     *
     * @param fh      输入流
     * @param sorInfo 存储结果的对象
     * @return 状态字符串
     */
    public static String process(BufferedInputStream fh, SorInfo sorInfo) {
        final String bname = "FxdParams";
        int hsize = bname.length() + 1; // 包含尾部的 '\0'
        Block refBlock = null;
        String status = "nok";

        try {
            // 获取块的引用
            refBlock = sorInfo.getBlocks().getFxdParams();
            long startpos = (long) refBlock.getPos();
            fh.reset();
            fh.skip(startpos);  // 跳过块的起始位置
        } catch (Exception ex) {
            return status;
        }

        int format = (int) sorInfo.getFormat();

        if (format == 2) {
            // 读取并验证头部
            byte[] buffer = new byte[hsize];
            try {
                fh.read(buffer, 0, hsize);
            } catch (IOException e) {
                return status;
            }
            String mystr = new String(buffer, StandardCharsets.US_ASCII);
            if (!mystr.equals(bname + "\0")) {
                return status;
            }
        }

        // 初始化 FxdParams 字典
        sorInfo.setFxdParams(new FxdParamsVo());

        // 定义字段列表
        List<FieldDefinition> plist = format == 1 ? getFormat1Fields() : getFormat2Fields();

        // 处理字段
        status = processFields(fh, plist, sorInfo);

        // 读取块的剩余部分（如果需要）
        long endpos = (long) sorInfo.getBlocks().getFxdParams().getPos()
                + (long) sorInfo.getBlocks().getFxdParams().getSize();

        try{
            fh.skip(endpos - fh.available());
        }
        catch (Exception e){
            return status;
        }

        status = "ok";
        return status;
    }

    /**
     * 获取格式 1 的字段列表
     */
    private static List<FieldDefinition> getFormat1Fields() {
        List<FieldDefinition> fields = new ArrayList<>();
        fields.add(new FieldDefinition("date/time", 0, 4, "v", null, null, ""));
        fields.add(new FieldDefinition("unit", 4, 2, "s", null, null, ""));
        fields.add(new FieldDefinition("wavelength", 6, 2, "v", 0.1, 1, "nm"));
        fields.add(new FieldDefinition("acquisition offset", 8, 4, "i", null, null, ""));
        fields.add(new FieldDefinition("number of pulse width entries", 12, 2, "v", null, null, ""));
        fields.add(new FieldDefinition("pulse width", 14, 2, "v", null, 0, "ns"));
        fields.add(new FieldDefinition("sample spacing", 16, 4, "v", 1e-8, null, "usec"));
        fields.add(new FieldDefinition("num data points", 20, 4, "v", null, null, ""));
        fields.add(new FieldDefinition("index", 24, 4, "v", 1e-5, 6, ""));
        fields.add(new FieldDefinition("BC", 28, 2, "v", -0.1, 2, "dB"));
        fields.add(new FieldDefinition("num averages", 30, 4, "v", null, null, ""));
        fields.add(new FieldDefinition("range", 34, 4, "v", 2e-5, 6, "km"));
        fields.add(new FieldDefinition("front panel offset", 38, 4, "i", null, null, ""));
        fields.add(new FieldDefinition("noise floor level", 42, 2, "v", null, null, ""));
        fields.add(new FieldDefinition("noise floor scaling factor", 44, 2, "i", null, null, ""));
        fields.add(new FieldDefinition("power offset first point", 46, 2, "v", null, null, ""));
        fields.add(new FieldDefinition("loss thr", 48, 2, "v", 0.001, 3, "dB"));
        fields.add(new FieldDefinition("refl thr", 50, 2, "v", -0.001, 3, "dB"));
        fields.add(new FieldDefinition("EOT thr", 52, 2, "v", 0.001, 3, "dB"));
        return fields;
    }

    /**
     * 获取格式 2 的字段列表
     */
    private static List<FieldDefinition> getFormat2Fields() {
        List<FieldDefinition> fields = new ArrayList<>();
        fields.add(new FieldDefinition("date/time", 0, 4, "v", null, null, ""));
        fields.add(new FieldDefinition("unit", 4, 2, "s", null, null, ""));
        fields.add(new FieldDefinition("wavelength", 6, 2, "v", 0.1, 1, "nm"));
        fields.add(new FieldDefinition("acquisition offset", 8, 4, "i", null, null, ""));
        fields.add(new FieldDefinition("acquisition offset distance", 12, 4, "i", null, null, ""));
        fields.add(new FieldDefinition("number of pulse width entries", 16, 2, "v", null, null, ""));
        fields.add(new FieldDefinition("pulse width", 18, 2, "v", null, 0, "ns"));
        fields.add(new FieldDefinition("sample spacing", 20, 4, "v", 1e-8, null, "usec"));
        fields.add(new FieldDefinition("num data points", 24, 4, "v", null, null, ""));
        fields.add(new FieldDefinition("index", 28, 4, "v", 1e-5, 6, ""));
        fields.add(new FieldDefinition("BC", 32, 2, "v", -0.1, 2, "dB"));
        fields.add(new FieldDefinition("num averages", 34, 4, "v", null, null, ""));
        fields.add(new FieldDefinition("averaging time", 38, 2, "v", 0.1, 0, "sec"));
        fields.add(new FieldDefinition("range", 40, 4, "v", 2e-5, 6, "km"));
        fields.add(new FieldDefinition("acquisition range distance", 44, 4, "i", null, null, ""));
        fields.add(new FieldDefinition("front panel offset", 48, 4, "i", null, null, ""));
        fields.add(new FieldDefinition("noise floor level", 52, 2, "v", null, null, ""));
        fields.add(new FieldDefinition("noise floor scaling factor", 54, 2, "i", null, null, ""));
        fields.add(new FieldDefinition("power offset first point", 56, 2, "v", null, null, ""));
        fields.add(new FieldDefinition("loss thr", 58, 2, "v", 0.001, 3, "dB"));
        fields.add(new FieldDefinition("refl thr", 60, 2, "v", -0.001, 3, "dB"));
        fields.add(new FieldDefinition("EOT thr", 62, 2, "v", 0.001, 3, "dB"));
        fields.add(new FieldDefinition("trace type", 64, 2, "s", null, null, ""));
        fields.add(new FieldDefinition("X1", 66, 4, "i", null, null, ""));
        fields.add(new FieldDefinition("Y1", 70, 4, "i", null, null, ""));
        fields.add(new FieldDefinition("X2", 74, 4, "i", null, null, ""));
        fields.add(new FieldDefinition("Y2", 78, 4, "i", null, null, ""));
        return fields;
    }

    /**
     * 处理字段
     */
    private static String processFields(BufferedInputStream fh, List<FieldDefinition> plist, SorInfo sorInfo) {
        final String bname = "FxdParams";
        FxdParamsVo xref = sorInfo.getFxdParams();

        for (FieldDefinition field : plist) {
            String name = field.getName();
            int fsize = field.getSize();
            String ftype = field.getType();
            Double scale = field.getScale();
            Integer dgt = field.getPrecision();
            String unit = field.getUnit();
            String xstr = "";

            try {
                if (ftype.equals("i")) {
                    int val = (int) Parts.getSigned(fh, fsize);
                    xstr = String.valueOf(val);
                } else if (ftype.equals("v")) {
                    double val = Parts.getUInt(fh, fsize);
                    if (scale != null) {
                        val = val * scale;
                    }
                    if (dgt != null) {
                        xstr = String.format("%." + dgt + "f", val);
                    } else {
                        xstr = String.valueOf(val);
                    }
                } else if (ftype.equals("h")) {
                    xstr = Parts.getHex(fh, fsize);
                } else if (ftype.equals("s")) {
                    byte[] buffer = new byte[fsize];
                    fh.read(buffer, 0, fsize);
                    xstr = new String(buffer, StandardCharsets.UTF_8).trim();
                } else {
                    byte[] buffer = new byte[fsize];
                    fh.read(buffer, 0, fsize);
                    xstr = bytesToHex(buffer);
                }

                // 处理特殊字段
                if (name.equals("date/time")) {
                    long val = (long)Double.parseDouble(xstr);
                    LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(val), ZoneOffset.systemDefault());
                    xstr = dateTime.format(DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy")) + " (" + val + " sec)";
                } else if (name.equals("unit")) {
                    xstr += UnitMap.getOrDefault(xstr, "");
                } else if (name.equals("trace type")) {
                    xstr += TraceType.getOrDefault(xstr, "");
                }

                // 处理多个脉冲宽度条目的情况
                if (name.equals("number of pulse width entries") && (int)Double.parseDouble(xstr) > 1) {
                    System.exit(1); // 退出程序
                }

                switch (name){
                    case "date/time":
                        xref.setDateTime(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "unit":
                        xref.setUnit(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "wavelength":
                        xref.setWavelength(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "acquisition offset":
                        xref.setAcquisitionOffset(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "acquisition offset distance":
                        xref.setAcquisitionOffsetDistance(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "number of pulse width entries":
                        xref.setNumberOfPulseWidthEntries(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "pulse width":
                        xref.setPulseWidth(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "sample spacing":
                        xref.setSampleSpacing(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "num data points":
                        xref.setNumDataPoints(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "index":
                        xref.setIndex(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "BC":
                        xref.setBc(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "num averages":
                        xref.setNumAverages(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "averaging time":
                        xref.setAveragingTime(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "range":
//                        xref.setRange(Double.parseDouble(unit.isEmpty() ? xstr : xstr + " " + unit));
                        xref.setRange(Double.parseDouble(xstr));
                        break;
                    case "acquisition range distance":
                        xref.setAcquisitionRangeDistance(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "front panel offset":
                        xref.setFrontPanelOffset(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "noise floor level":
                        xref.setNoiseFloorLevel(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "noise floor scaling factor":
                        xref.setNoiseFloorScalingFactor(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "power offset first point":
                        xref.setPowerOffsetFirstPoint(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "loss thr":
                        xref.setLossThr(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "refl thr":
                        xref.setReflThr(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "EOT thr":
                        xref.setEotThr(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "trace type":
                        xref.setTraceType(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "X1":
                        xref.setX1(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "Y1":
                        xref.setY1(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "X2":
                        xref.setX2(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;
                    case "Y2":
                        xref.setY2(unit.isEmpty() ? xstr : xstr + " " + unit);
                        break;


                }
            } catch (IOException e) {
                e.printStackTrace();
                return "nok";
            }
        }

        // 计算分辨率和范围
        double ior = Double.parseDouble(xref.getIndex());
        double ss = Double.parseDouble(xref.getSampleSpacing().split(" ")[0]);
        double dx = ss * Parts.Sol / ior;
        xref.setRange(dx * (int)Double.parseDouble(xref.getNumDataPoints()));
        xref.setResolution(dx * 1000.0);  // 转换为米

        return "ok";
    }

    /**
     * 将字节数组转换为十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }

    /**
     * 字段定义
     */
    private static class FieldDefinition {
        private final String name;
        private final int startPos;
        private final int size;
        private final String type;
        private final Double scale;
        private final Integer precision;
        private final String unit;

        public FieldDefinition(String name, int startPos, int size, String type, Double scale, Integer precision, String unit) {
            this.name = name;
            this.startPos = startPos;
            this.size = size;
            this.type = type;
            this.scale = scale;
            this.precision = precision;
            this.unit = unit;
        }

        public String getName() {
            return name;
        }

        public int getStartPos() {
            return startPos;
        }

        public int getSize() {
            return size;
        }

        public String getType() {
            return type;
        }

        public Double getScale() {
            return scale;
        }

        public Integer getPrecision() {
            return precision;
        }

        public String getUnit() {
            return unit;
        }
    }
}
